﻿#region Using

using System;
using System.Collections.Generic;
using System.Drawing;

using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;

#endregion

namespace EigenFaces {
	internal class FaceRecognizer {

		private const int FaceSize = 256;
		private const int ScaleFactor = 4;

		private Image<Gray, byte> _image;
		private List<Rectangle> _regionsOfInterest = new List<Rectangle>();

		public FaceRecognizer(string filename) : this(new Image<Bgr, byte>(filename)) { }

		public FaceRecognizer(Image<Bgr, byte> image) {
			_image = image.Convert<Gray, byte>();
			FindFaces();
		}

		public static HaarCascade FaceCascade { get; set; }
		public static HaarCascade EyesCascade { get; set; }

		public int NumberOfFaces {
			get { return _regionsOfInterest.Count; }
		}

		private void FindFaces() {
			var scaled = _image.Resize(1.0 / ScaleFactor, INTER.CV_INTER_AREA);

			var faces = FaceCascade.Detect(scaled,
										   1.2,
										   2,
										   HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
										   new Size(FaceSize / ScaleFactor, FaceSize / ScaleFactor));

			foreach( var face in faces ) {
				Rectangle roi = face.rect.Scale(ScaleFactor);
				var eyes = EyesCascade.Detect(_image.GetSubRect(roi),
											  1.2,
											  8,
											  HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
											  new Size(roi.Width / 8, roi.Height / 8));
				if( eyes.Length == 2 && CheckEyePosition(roi, eyes[0].rect, eyes[1].rect) ) {
					_regionsOfInterest.Add(roi);
				}
			}
		}

		private bool CheckEyePosition(Rectangle roi, Rectangle eye1, Rectangle eye2) {
			Point middleOfFace = roi.MiddlePoint();
			Point middleEye1 = eye1.Move(roi.Location).MiddlePoint();
			Point middleEye2 = eye2.Move(roi.Location).MiddlePoint();

			int vertDiff = Math.Abs(middleEye1.Y - middleEye2.Y);
			int middleOfEyes = (middleEye1.X + middleEye2.X) / 2;
			int horiDiff = Math.Abs(middleOfFace.X - middleOfEyes);

			return (vertDiff < (roi.Height / 40)) && (horiDiff < (roi.Width / 25));
		}

		public Rectangle getRegionOfInterest(int index) {
			return _regionsOfInterest[index];
		}

		public Image<Gray, double> getFaceImage(int index) {
			return
				_image.GetSubRect(_regionsOfInterest[index]).Resize(FaceSize, FaceSize, INTER.CV_INTER_AREA).Convert<Gray, double>();
		}
	}
}
